package triannon.client.components;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.util.HashSet;

import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;

import triannon.client.util.timer.TTimer;
import triannon.client.util.timer.TTimerListener;

@SuppressWarnings("serial")
public class TFrame extends JFrame implements TTimerListener {
	private Canvas canvas;
	private Menu menu;
	private MiniMap miniMap;
	private JPanel glassPane;
	private JPanel mainMenu;

	private Point mousePosition = new Point(50, 50);
	private int mouseSensitivityArea = 20;

	private HashSet<Integer> pressedKeys = new HashSet<Integer>();

	public TFrame() {
		super("Triannon");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		TTimer.getInstance().addSTimerListener(this);

		boolean fullScreen = false;

		// fullScreen = true;

		int w;
		int h;

		if (fullScreen) {
			w = Toolkit.getDefaultToolkit().getScreenSize().width;
			h = Toolkit.getDefaultToolkit().getScreenSize().height;
			setExtendedState(MAXIMIZED_BOTH);
			setUndecorated(true);
		} else {
			w = 800;
			h = 600;
		}

		setSize(w, h);
		setMinimumSize(new Dimension(w, h));
		setMaximumSize(new Dimension(w, h));

		setLocationRelativeTo(null);

		init();
		setupKeyBindungs();
	}

	private JPanel createButtonPanel() {
		JPanel panel = new JPanel();
		panel.setOpaque(true);
		panel.setLayout(new GridBagLayout());
		panel.setPreferredSize(new Dimension(500, 300));
		GridBagConstraints constraints = new GridBagConstraints();
		constraints.fill = GridBagConstraints.HORIZONTAL;
		constraints.gridx = 0;
		constraints.gridy = 0;
		constraints.weightx = 1.0d;
		constraints.weighty = 0.0d;
		constraints.anchor = GridBagConstraints.CENTER;
		constraints.insets = new Insets(15, 15, 15, 15);

		panel.add(new TButton(new ActionClose()), constraints);

		constraints.gridy++;

		panel.add(new TButton(new ActionShowOptions()), constraints);

		constraints.gridy++;

		panel.add(new TButton(new ActionShowHelp()), constraints);

		constraints.gridy++;

		panel.add(new TButton(new ActionExit()), constraints);

		return panel;
	}

	private void init() {
		glassPane = new GlassPane();
		setGlassPane(glassPane);
		glassPane.setVisible(false);
		glassPane.setSize(getSize());
		glassPane.setOpaque(true);
		glassPane.setLayout(new GridBagLayout());
		GridBagConstraints constraints = new GridBagConstraints();
		constraints.fill = GridBagConstraints.NONE;
		constraints.gridx = 0;
		constraints.gridy = 0;
		constraints.weightx = 1.0d;
		constraints.weighty = 1.0d;
		constraints.anchor = GridBagConstraints.CENTER;

		mainMenu = createButtonPanel();
		glassPane.add(mainMenu, constraints);

		ConsumingMouseListener listener = new ConsumingMouseListener();
		glassPane.addMouseMotionListener(listener);
		glassPane.addMouseListener(listener);
		glassPane.addMouseWheelListener(listener);

		setLayout(new GridBagLayout());
		constraints = new GridBagConstraints();
		constraints.fill = GridBagConstraints.BOTH;
		constraints.gridx = 1;
		constraints.gridy = 1;

		constraints.weightx = 1.0d;
		constraints.weighty = 1.0d;

		JScrollPane scrollPane = new JScrollPane();
		scrollPane
				.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);

		canvas = new Canvas(scrollPane);
		scrollPane.setViewportView(canvas);

		miniMap = new MiniMap(canvas, scrollPane, Menu.MENU_WIDTH - 10);
		miniMap.setOriginalSize(canvas.getPreferredSize().width, canvas
				.getPreferredSize().height);

		add(scrollPane, constraints);

		constraints.weightx = 0.0d;
		constraints.gridx++;

		menu = new Menu(miniMap);

		add(menu, constraints);
	}

	private void setupKeyBindungs() {
		getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
				KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "mainMenu");
		getRootPane().getActionMap().put("mainMenu", new ActionMainMenu());

		addKeyListener(new SKeyListener());
	}

	@Override
	public void update(long tpf) {
		// if the frame is not focused, do nothing
		if (!TFrame.this.isFocused()) {
			return;
		}

		mousePosition = MouseInfo.getPointerInfo().getLocation();
		mousePosition.x -= getX();
		mousePosition.y -= getY();

		int dirX = 0, dirY = 0;

		if (isScrollLeft()) {
			dirX = -1;
		} else if (isScrollRight()) {
			dirX = 1;
		}

		if (isScrollUp()) {
			dirY = -1;
		} else if (isScrollDown()) {
			dirY = 1;
		}

		if (dirX != 0 || dirY != 0) {
			move(tpf, dirX, dirY);
		}
	}

	private void move(long tpf, int dirX, int dirY) {
		int x = dirX * (int) tpf;
		int y = dirY * (int) tpf;

		canvas.drag(x, y);
		miniMap.repaint();
	}

	private boolean isScrollLeft() {
		return mousePosition.x < mouseSensitivityArea
				|| pressedKeys.contains(KeyEvent.VK_LEFT);
	}

	private boolean isScrollUp() {
		return mousePosition.y < mouseSensitivityArea
				|| pressedKeys.contains(KeyEvent.VK_UP);
	}

	private boolean isScrollRight() {
		return mousePosition.x > (getWidth() - mouseSensitivityArea)
				|| pressedKeys.contains(KeyEvent.VK_RIGHT);
	}

	private boolean isScrollDown() {
		return mousePosition.y > (getHeight() - mouseSensitivityArea)
				|| pressedKeys.contains(KeyEvent.VK_DOWN);
	}

	private class ActionMainMenu extends AbstractAction {
		public ActionMainMenu() {

		}

		@Override
		public void actionPerformed(ActionEvent e) {
			if (!glassPane.isVisible()) {
				glassPane.setVisible(true);
				mainMenu.requestFocusInWindow();
			} else {
				glassPane.setVisible(false);
			}
		}
	}

	private class ActionShowOptions extends AbstractAction {
		public ActionShowOptions() {
			super("Options");
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			JOptionPane.showMessageDialog(null, "Not implemented yet.",
					"Sorry, I can't do this", JOptionPane.INFORMATION_MESSAGE,
					new ImageIcon());
		}
	}

	private class ActionShowHelp extends AbstractAction {
		public ActionShowHelp() {
			super("Help");
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			JOptionPane.showMessageDialog(null,
					"I don't think you'll need help at this point!",
					"Sorry, I can't do this", JOptionPane.INFORMATION_MESSAGE,
					new ImageIcon());
		}
	}

	private class ActionClose extends AbstractAction {
		public ActionClose() {
			super("Close");
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			glassPane.setVisible(false);
		}
	}

	public class ActionExit extends AbstractAction {
		public ActionExit() {
			super("Exit");
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			System.exit(0);
		}
	}

	// private class FrameMouseListener extends MouseAdapter {
	// @Override
	// public void mouseMoved(MouseEvent e) {
	// mousePosition = e.getPoint();
	// redispatchMouseEvent(e, false);
	// }
	//
	// /*
	// * The mouseDragged, mouseClicked, mouseEntered, mouseExited, and
	// * mousePressed methods have the same implementation as mouseMoved.
	// */
	//
	// public void mouseDragged(MouseEvent e) {
	// redispatchMouseEvent(e, true);
	// }
	//
	// @Override
	// public void mouseClicked(MouseEvent e) {
	// redispatchMouseEvent(e, true);
	// }
	//
	// @Override
	// public void mouseEntered(MouseEvent e) {
	// redispatchMouseEvent(e, true);
	// }
	//
	// @Override
	// public void mouseExited(MouseEvent e) {
	// redispatchMouseEvent(e, true);
	// }
	//
	// @Override
	// public void mousePressed(MouseEvent e) {
	// redispatchMouseEvent(e, true);
	// }
	//
	// @Override
	// public void mouseReleased(MouseEvent e) {
	// redispatchMouseEvent(e, true);
	// }
	//
	// @Override
	// public void mouseWheelMoved(MouseWheelEvent e) {
	// redispatchMouseEvent(e, true);
	// }
	//
	// private void redispatchMouseEvent(MouseEvent e, boolean repaint) {
	// Point glassPanePoint = e.getPoint();
	// Container container = getContentPane();
	// Point containerPoint = SwingUtilities.convertPoint(glassPane,
	// glassPanePoint, container);
	//
	// Component component = SwingUtilities.getDeepestComponentAt(container,
	// containerPoint.x, containerPoint.y);
	//
	// if (component != null) {
	// // Forward events over the check box.
	// Point componentPoint = SwingUtilities.convertPoint(glassPane,
	// glassPanePoint, component);
	// component.dispatchEvent(new MouseEvent(component, e.getID(), e
	// .getWhen(), e.getModifiers(), componentPoint.x, componentPoint.y, e
	// .getClickCount(), e.isPopupTrigger()));
	// }
	// }
	// }

	private class SKeyListener extends KeyAdapter {
		@Override
		public void keyPressed(KeyEvent e) {
			System.err.println("pressed " + KeyEvent.getKeyText(e.getKeyCode()));

			pressedKeys.add(e.getKeyCode());
		}

		@Override
		public void keyReleased(KeyEvent e) {
			System.err.println("released " + KeyEvent.getKeyText(e.getKeyCode()));

			pressedKeys.remove((Integer) e.getKeyCode());
		}
	}

	private class ConsumingMouseListener extends MouseAdapter {
		@Override
		public void mouseMoved(MouseEvent e) {
			e.consume();
		}

		public void mouseDragged(MouseEvent e) {
			e.consume();
		}

		@Override
		public void mouseClicked(MouseEvent e) {
			e.consume();
		}

		@Override
		public void mouseEntered(MouseEvent e) {
			e.consume();
		}

		@Override
		public void mouseExited(MouseEvent e) {
			e.consume();
		}

		@Override
		public void mousePressed(MouseEvent e) {
			e.consume();
		}

		@Override
		public void mouseReleased(MouseEvent e) {
			e.consume();
		}

		@Override
		public void mouseWheelMoved(MouseWheelEvent e) {
			e.consume();
		}
	}
}
